import datetime, os, re, shutil, sys, traceback
import matplotlib
matplotlib.use('AGG')
import pylab
import settings

try: set()
except NameError: from sets import Set as set

RUN_FROM_DIR = os.path.abspath(os.path.dirname(sys.argv[0]))
BUG_RE = re.compile("bug[:# ]*([0-9]+)", re.IGNORECASE)

class PyStatCVS(object):


  def __init__(self, CVSROOT, VIEWVC):
    self.CVSROOT = CVSROOT
    self.VIEWVC = VIEWVC
    self.files = set()
    self.commit_log = []
    self.author_commit_logs = {}
    self.project_commit_logs = {}
    self.author_project_commit_logs = {}
    self.authors = set()
    self.projects = set()
    
    
  def _get_projects(self):
    projects = list(self.projects)
    projects.sort()
    return projects


  def _get_authors(self):
    authors = list(self.authors)
    authors.sort()
    return authors


  def _get_active_authors(self):
    active_authors = []
    for author in self._get_authors():
      if self._get_author_commit_log(author):
        if self._get_author_commit_log(author)[0][0][0] > datetime.datetime.now() - datetime.timedelta(365):
          active_authors.append(author)
    return active_authors


  def _get_author_commit_log(self, author):
    if author not in self.author_commit_logs:
      author_commit_log = []
      for tuple_date_author_log, files in self.clustered_commit_log:
        if tuple_date_author_log[1]==author:
          author_commit_log.append( (tuple_date_author_log, files) )
      self.author_commit_logs[author] = author_commit_log
    return self.author_commit_logs[author]
      
      
  def _get_project_commit_log(self, project):
    if project not in self.project_commit_logs:
      project_commit_log = []
      for tuple_date_author_log, files in self.clustered_commit_log:
        if project in [ file.split(os.sep)[0] for file,rev1,diff_size,rev2,diff in files ]:
          project_commit_log.append( (tuple_date_author_log, files) )
      self.project_commit_logs[project] = project_commit_log
    return self.project_commit_logs[project]
      
      
  def _get_author_project_commit_log(self, author, project):
    if (author,project) not in self.author_project_commit_logs:
      author_project_commit_log = []
      for tuple_date_author_log, files in self._get_author_commit_log(author):
        if project in [ file.split(os.sep)[0] for file,rev1,diff_size,rev2,diff in files ]:
          author_project_commit_log.append( (tuple_date_author_log, files) )
      self.author_project_commit_logs[(author,project)] = author_project_commit_log
    return self.author_project_commit_logs[(author,project)]
      

  def get_stats(self):
    for dirpath, dirnames, filenames in os.walk(self.CVSROOT):
      for filename in filenames:
        if filename.endswith(',v'):
          try:
            filename, rev_map = self._get_file_stats( os.path.join(dirpath, filename) )
            self.files.add(filename)
            self._update_commit_logs( filename, rev_map )
          except:
            print ' ^^ failed ^^'
            traceback.print_exc()
            return
            traceback.print_exc()
    self.commit_log.sort()
    self.commit_log.reverse()
    
    self.clustered_commit_log = []
    last_tuple_date_author_log, values = None, None
    for x in self.commit_log:
      tuple_date_author_log = tuple(x[:3])
      if last_tuple_date_author_log  != tuple_date_author_log:
        if last_tuple_date_author_log is not None:
          self.clustered_commit_log.append( (last_tuple_date_author_log, values) )
          #print last_tuple_date_author_log, values
        values = []
        last_tuple_date_author_log = tuple_date_author_log
      values.append( x[3:] )
    
    
  def _gen_report_index(self, outdir):
    f = HTMLFile( os.path.join(outdir,'index.html'), 'w' )

    f.write('<div style="float:right;">')
    f.write('<a href="commit_graph.png"><img src="commit_graph.png" width="256" border=0></a> ')
    f.write('<a href="loc_graph.png"><img src="loc_graph.png" width="256" border=0></a> ')
    f.write('<br>')
    self._get_hour_of_day_hist( self.clustered_commit_log, os.path.join(outdir,'hour_of_day_hist.png') )
    f.write('<a href="%s"><img src="%s" width="256" border="0"></a> ' % ('hour_of_day_hist.png', 'hour_of_day_hist.png'))
    self._get_day_of_week_hist( self.clustered_commit_log, os.path.join(outdir,'day_of_week_hist.png') )
    f.write('<a href="%s"><img src="%s" width="256" border=0></a> ' % ('day_of_week_hist.png', 'day_of_week_hist.png'))
    f.write('<br>')
    self._authors_commit_pie( self.clustered_commit_log, os.path.join(outdir,'authors_commit_pie.png') )
    f.write('<a href="%s"><img src="%s" width="256" border=0></a> ' % ('authors_commit_pie.png', 'authors_commit_pie.png'))
    self._projects_commit_pie( self.clustered_commit_log, os.path.join(outdir,'projects_commit_pie.png') )
    f.write('<a href="%s"><img src="%s" width="256" border=0></a> ' % ('projects_commit_pie.png', 'projects_commit_pie.png'))
    f.write('</div>')
    
    f.write('<h2>Stats</h2>')
    f.write('<ul>')
    f.write('<li>Developers: <b>%i</b></li>' % ( len(self._get_authors()) ))
    f.write('<li>Projects: <b>%i</b></li>' % ( len(self._get_projects()) ))
    f.write('<li>Commits: <b>%i</b></li>' % ( len(self.clustered_commit_log) ))
    if self.clustered_commit_log:
      oldest_commit = min([ x[0][0] for x in self.clustered_commit_log ])
      newest_commit = max([ x[0][0] for x in self.clustered_commit_log ])
      f.write('<li>Oldest commit: <b>%s</b></li>' % oldest_commit)
      f.write('<li>Most recent commit: <b>%s</b></li>' % newest_commit)
      f.write('<li>Timespan: <b>%s</b></li>' % ( newest_commit - oldest_commit ))
    f.write('</ul>')

    f.write('<div style="float:left; margin:1em;">')
    f.write('<h2>Developers</h2>')
    f.write('<ul>')
    for author in self._get_authors():
      f.write('<li><a href="%s">%s</a></li>' % ('author_%s.html'%author, author))
    f.write('</ul>')
    f.write('</div>')
    f.write('<div style="float:left; margin:1em;">')
    f.write('<h2>Projects</h2>')
    f.write('<ul>')
    for project in self._get_projects():
      f.write('<li><a href="%s">%s</a></li>' % ('project_%s.html'%project, project))
    f.write('</ul>')
    f.write('</div>')
    f.write('<div style="float:left; margin:1em;">')
    f.write('<h2>Special Pages</h2>')
    f.write('<ul>')
    f.write('<li><a href="commit_log.html">full commit log</a></li>')
    f.write('</ul>')
    f.write('</div>')
    f.close()
    
    
  def _gen_commit_graph(self, outdir):
    # gen the commit plot
    pylab.cla()
    c = 0
    colors = ['b','g','r','c','m','y','k',]
    for author in self._get_active_authors():
      t = [ x[0][0] for x in self._get_author_commit_log(author) ]
      t.reverse()
      if c>=2*len(colors): linestyle = ':'
      elif c>=len(colors): linestyle = '--'
      else: linestyle = '-'
      pylab.plot(t, range(len(t)), label=author, linestyle=linestyle, color=colors[c%len(colors)])
      c += 1
    # shrink the axis labels
    ax = pylab.axes()
    for xlabel_i in ax.get_xticklabels():
      xlabel_i.set_fontsize(8) 
    # show the legend
    pylab.legend(loc='upper left', prop=matplotlib.font_manager.FontProperties(size=9))
    pylab.xlabel('Date')
    pylab.ylabel('# of Commits')
    pylab.title('Commit History for All Developers and Projects')
    pylab.grid(True)
    pylab.savefig(os.path.join(outdir,'commit_graph'))
    

  def _gen_loc_graph(self, outdir, project=None):
    # gen the commit plot
    pylab.cla()
    c = 0
    colors = ['b','g','r','c','m','y','k',]
    for author in self._get_active_authors():
      t = []
      loc = 0
      y = []
      x = self._get_author_commit_log(author)
      x.reverse()
      for commit, files in x:
        commit_loc = 0
        for f in files:
          if f[2]:
            commit_loc += int(f[2][0]) + int(f[2][1]) + int(f[2][2])
        if True or commit_loc<1000:
          loc += commit_loc
          t.append( commit[0] )
          y.append(loc)
      if c>=2*len(colors): linestyle = ':'
      elif c>=len(colors): linestyle = '--'
      else: linestyle = '-'
      pylab.plot(t, y, label=author, linestyle=linestyle, color=colors[c%len(colors)])
      c += 1
    # shrink the axis labels
    ax = pylab.axes()
    for xlabel_i in ax.get_xticklabels():
      xlabel_i.set_fontsize(8) 
    # show the legend
    pylab.legend(loc='upper left', prop=matplotlib.font_manager.FontProperties(size=9))
    pylab.xlabel('Date')
    pylab.ylabel('# of Lines of Code')
    pylab.title('LOC History for All Developers and Projects')
    pylab.grid(True)
    pylab.savefig(os.path.join(outdir,'loc_graph'))
    


  def _gen_report_commit_log(self, outdir):
    f = HTMLFile( os.path.join(outdir,'commit_log.html'), 'w' )

    f.write('<h2>Stats</h2>')
    f.write('<ul>')
    if len(self.clustered_commit_log):
      f.write('<li>Number of commits: <b>%i</b></li>' % len(self.clustered_commit_log))
      oldest_commit = min([ x[0][0] for x in self.clustered_commit_log ])
      newest_commit = max([ x[0][0] for x in self.clustered_commit_log ])
      f.write('<li>First commit: <b>%s</b></li>' % oldest_commit)
      f.write('<li>Most recent commit: <b>%s</b></li>' % newest_commit)
      f.write('<li>Timespan: <b>%s</b></li>' % (newest_commit - oldest_commit))
    f.write('</ul>')

    f.write('<h2>Full Commit Log</h2>')
    self._write_paged_commit_log( self.clustered_commit_log, f )
    f.close()


  def _gen_report_author(self, outdir, author):
      fa = HTMLFile( os.path.join(outdir,'author_%s.html'%author), 'w' )
      fa.write('<h1>Developer: %s</h1>'%author)

      author_projects = set()
      for tuple_date_author_log, files in self._get_author_commit_log(author):
        for x in files:
          file = x[0]
          author_projects.add(file.split(os.sep)[0])
      author_projects = list(author_projects)
      author_projects.sort()
      
      # gen per-dev graphs
      fa.write('<div style="float:right;">')
      self._get_hour_of_day_hist( self._get_author_commit_log(author), os.path.join(outdir,'author_%s_hour_of_day_hist.png'%author) )
      fa.write('<a href="%s"><img src="%s" width="256" border="0"></a> ' % ('author_%s_hour_of_day_hist.png'%author, 'author_%s_hour_of_day_hist.png'%author))
      self._get_day_of_week_hist( self._get_author_commit_log(author), os.path.join(outdir,'author_%s_day_of_week_hist.png'%author) )
      fa.write('<a href="%s"><img src="%s" width="256" border=0></a> ' % ('author_%s_day_of_week_hist.png'%author, 'author_%s_day_of_week_hist.png'%author))
      fa.write('<br>')
      self._projects_commit_pie( self._get_author_commit_log(author), os.path.join(outdir,'author_%s_projects_commit_pie.png'%author) )
      fa.write('<a href="%s"><img src="%s" width="256" border=0></a> ' % ('author_%s_projects_commit_pie.png'%author, 'author_%s_projects_commit_pie.png'%author))
      fa.write('</div>')

      fa.write('<h2>Stats</h2>')
      fa.write('<ul>')
      fa.write('<li>Number of projects: <b>%i</b></li>' % len(author_projects))
      if len(self._get_author_commit_log(author)):
        fa.write('<li>Number of commits: <b>%i</b></li>' % len(self._get_author_commit_log(author)))
        oldest_commit = min([ x[0][0] for x in self._get_author_commit_log(author) ])
        newest_commit = max([ x[0][0] for x in self._get_author_commit_log(author) ])
        fa.write('<li>Oldest commit: <b>%s</b></li>' % oldest_commit)
        fa.write('<li>Most recent commit: <b>%s</b></li>' % newest_commit)
        fa.write('<li>Timespan: <b>%s</b></li>' % ( newest_commit - oldest_commit ))
      fa.write('</ul>')

      fa.write('<h2>Projects</h2>')
      #fa.write('<p>Developer <b><a href="author_%s.html">%s</a></b> has contributed to the following projects:</p>'%(author,author))

      fa.write('<table border=1><tr><th>Project</th><th>Commits</th></tr>')
      for project in author_projects:
        fa.write('<tr><td><a href="%s">%s</a></td><td>%i</td></tr>' % ('project_%s_author_%s.html'%(project,author), project, len(self._get_author_project_commit_log(author, project))))
      fa.write('</table>')

      fa.write('<h2>Commit Log</h2>')
      commit_log = list(self._get_author_commit_log(author))
      commit_log.reverse()
      self._write_paged_commit_log( commit_log, fa )
      fa.close()
      
      
  def _get_hour_of_day_hist( self, commits, fn ):
    print 'generating:', fn
    pylab.cla()
    hour_counts = [ len([ x[0][0].hour for x in commits if x[0][0].hour==i ]) for i in range(24) ]
    pylab.bar(range(24), hour_counts, align='center')
    pylab.xlabel('Hour of the Day')
    pylab.ylabel('# of Commits')
    pylab.title('Commits by Time of Day')
    pylab.savefig(fn, dpi=64)  


  def _get_day_of_week_hist( self, commits, fn ):
    print 'generating:', fn
    pylab.cla()
    day_counts = [ len([ x[0][0].weekday() for x in commits if x[0][0].weekday()==i ]) for i in range(7) ]
    pylab.bar(range(7), day_counts, align='center')
    pylab.xlabel('Day of the Week')
    pylab.ylabel('# of Commits')
    pylab.title('Commits by Day of the Week')
    pylab.savefig(fn, dpi=64)  


  def _authors_commit_pie( self, commits, fn ):
    print 'generating:', fn
    pylab.cla()
    authors = list(set( [ x[0][1] for x in commits ] ))
    authors.sort()
    author_counts = [ len([ x for x in commits if x[0][1]==author ]) for author in authors ]
    pylab.pie(author_counts, labels=authors)
    #pylab.xlabel('Author Contributions')
    #pylab.ylabel('# of Commits')
    pylab.title('Relative Author Contributions')
    pylab.savefig(fn, dpi=64)  


  def _projects_commit_pie( self, commits, fn ):
    print 'generating:', fn
    pylab.cla()
    projects = list(set( [ x[1][0][0].split(os.sep)[0] for x in commits ] ))
    projects.sort()
    projects_counts = [ len([ x for x in commits if x[1][0][0].startswith(project+os.sep) ]) for project in projects ]
    pylab.pie(projects_counts, labels=projects)
    #pylab.xlabel('Author Contributions')
    #pylab.ylabel('# of Commits')
    pylab.title('Relative Project Sizes')
    pylab.savefig(fn, dpi=64)  


  def _gen_report_project(self, outdir, project):
      project_authors = list( set( [ tuple_date_author_log[1] for tuple_date_author_log, files in self._get_project_commit_log(project) ] ) )
      project_authors.sort()
      fp = HTMLFile( os.path.join(outdir,'project_%s.html'%project), 'title' )
      fp.write('<h1>Project: %s</h1>'%project)

      # gen per-project graphs
      fp.write('<div style="float:right;">')
      self._get_hour_of_day_hist( self._get_project_commit_log(project), os.path.join(outdir,'project_%s_hour_of_day_hist.png'%project) )
      fp.write('<a href="%s"><img src="%s" width="256" border="0"></a> ' % ('project_%s_hour_of_day_hist.png'%project, 'project_%s_hour_of_day_hist.png'%project))
      self._get_day_of_week_hist( self._get_project_commit_log(project), os.path.join(outdir,'project_%s_day_of_week_hist.png'%project) )
      fp.write('<a href="%s"><img src="%s" width="256" border=0></a> ' % ('project_%s_day_of_week_hist.png'%project, 'project_%s_day_of_week_hist.png'%project))
      fp.write('<br>')
      self._authors_commit_pie( self._get_project_commit_log(project), os.path.join(outdir,'project_%s_authors_commit_pie.png'%project) )
      fp.write('<a href="%s"><img src="%s" width="256" border=0></a> ' % ('project_%s_authors_commit_pie.png'%project, 'project_%s_authors_commit_pie.png'%project))
      fp.write('</div>')

      fp.write('<h2>Stats</h2>')
      fp.write('<ul>')
      fp.write('<li>Number of developers: <b>%i</b></li>' % len(project_authors))
      if len(self._get_project_commit_log(project)):
        fp.write('<li>Number of commits: <b>%i</b></li>' % len(self._get_project_commit_log(project)))
        oldest_commit = min([ x[0][0] for x in self._get_project_commit_log(project) ])
        newest_commit = max([ x[0][0] for x in self._get_project_commit_log(project) ])
        fp.write('<li>Project born on: <b>%s</b></li>' % oldest_commit)
        fp.write('<li>Most recent commit: <b>%s</b></li>' % newest_commit)
        fp.write('<li>Timespan: <b>%s</b></li>' % (newest_commit - oldest_commit))
      fp.write('</ul>')
      fp.write('<h2>Developers Contributing to This Project</h2>')
      fp.write('<table border=1><tr><th>Developer</th><th>Commits</th></tr>')
      for author in project_authors:
        fp.write('<tr><td><a href="%s">%s</a></td><td>%i</td></tr>' % ('project_%s_author_%s.html'%(project,author), author, len(self._get_author_project_commit_log(author, project))))
      fp.write('</table>')

      fp.write('<h2>Commit Log</h2>')
      self._write_paged_commit_log( self._get_project_commit_log(project), fp )
      fp.close()


  def _gen_report_project_author(self, outdir, project, author):
    fa = HTMLFile( os.path.join(outdir,'project_%s_author_%s.html'%(project,author)), 'test' )
    fa.write('<h1>Developer: %s<br>Project: %s</h1>'%(author,project))
    fa.write('<p>Developer <b><a href="author_%s.html">%s\'s</a></b> contributions to project <b><a href="project_%s.html">%s</a></b>...</p>'%(author,author,project,project))
    fa.write('<h2>Commit Log</h2>')
    self._write_paged_commit_log( self._get_author_project_commit_log(author,project), fa )
    fa.close()

    
  def gen_report(self, outdir):
    try: os.mkdir(outdir)
    except: pass
    shutil.copy(os.path.join(RUN_FROM_DIR, 'pystatcvs.css'),outdir)
    
    self._gen_report_index(outdir)
    self._gen_report_commit_log(outdir)
    
    self._gen_commit_graph(outdir)
    self._gen_loc_graph(outdir)
    
    for author in self._get_authors():
      self._gen_report_author(outdir, author)
      
    for project in self._get_projects():
      self._gen_report_project(outdir, project)

    for project in self._get_projects():
      for author in self._get_authors():
        self._gen_report_project_author(outdir, project, author)


  def _write_paged_commit_log(self, commit_log, f):
    PAGE_SIZE = 256
    f.write('<dl class="commitlist">')
    count = 0
    for tuple_date_author_log, files in commit_log:
      count += 1
      if count%PAGE_SIZE==0:
        f.write('</dl>')
        f.write('<p style="font-size:smaller;"><a href="#" onclick="document.getElementById(\'commitlist%i\').style.display= document.getElementById(\'commitlist%i\').style.display==\'none\' ? \'\' : \'none\'; return false;">show/hide commits %i - %i</a></p>' % ( count//PAGE_SIZE, count//PAGE_SIZE, count, min( count + PAGE_SIZE, len(commit_log) ) ))
        f.write('<dl class="commitlist" style="display:none;" id="commitlist%i">' % (count//PAGE_SIZE))
        
      self._write_c_dl(tuple_date_author_log, files, f)
    f.write('</dl>')
      
  def _write_c_dl(self, tuple_date_author_log, files, f):
    f.write('<dt>')
    f.write('<b><a href="author_%s.html">%s</a></b> '%(tuple_date_author_log[1],tuple_date_author_log[1]))
    f.write('<span class="date">%s</span>'%tuple_date_author_log[0])
    f.write('</dt>\n')
    f.write('<dd>')

    commit_msg = tuple_date_author_log[2]
    bug_matches = list(BUG_RE.finditer(commit_msg))
    bug_matches.reverse()
    for match in bug_matches:
      bug = match.group(0)
      bug_id = match.group(1)
      commit_msg = commit_msg[:match.start(0)] + '<a href="%s%s">%s</a>'%(settings.BUGZILLA_BASE_URL,bug_id,bug) + commit_msg[match.end(0):]

    f.write('<p class="comment">%s</p>'%commit_msg)
    f.write('<p class="commitdetails">')
    
    for x in files:
      diff_id = "diff_"+ str(tuple_date_author_log[:2] + x[:1]).replace("'",'')
#      print 'diff_id', diff_id
      if not x[3]:
        f.write('<a href="%s">%s</a> '%(self.VIEWVC+x[0],x[0]))
        f.write('<span style="color:green;">(created)</span>')
      else:
        f.write('<a href="%s?r1=%s&r2=%s">%s</a> '%(self.VIEWVC+x[0],x[3],x[1],x[0]))
        f.write('r%s &rArr; r%s'%(x[3],x[1]))
        f.write('  (<span style="color:green;">+%i</span> ~%i <span style="color:red;">-%i</span>)'%x[2])
        f.write(' <a href="#" onclick="document.getElementById(\'%s\').style.display=document.getElementById(\'%s\').style.display==\'none\'?\'\':\'none\'; return false;">diff</a><br>'%(diff_id,diff_id,))
      f.write(' <textarea cols="80" rows="20" id="%s" style="display:none;">%s</textarea>'%(diff_id,x[4]))
    f.write('</p>')
    f.write('</dd>\n')
          
          
  def _update_commit_logs( self, filename, rev_map ):
    for rev, rev_info in rev_map.iteritems():
      log_info = ( rev_info['date'], rev_info['author'], rev_info['log'], filename, rev, rev_info['loc'], rev_info['prev'] , rev_info['diff'] )
      
      self.commit_log.append( log_info )
      
      author = rev_info['author']
      self.authors.add(author)
      
      project = filename.split(os.sep)[0]
      self.projects.add(project)
  
  
  def _get_file_stats(self, path):
    filename = path[len(self.CVSROOT):-2]
    #if not filename.endswith('.java'): return filename, {}
    print 'reading:', path
    
    f = open(path,'r')
    txt = f.read()
    f.close()
    
    header_revs_sep_index = txt.index('\n\n')
    revs_diffs_sep_index = txt.index('\ndesc\n')
    header_txt = txt[:header_revs_sep_index].strip()
    revs_txt = txt[header_revs_sep_index:revs_diffs_sep_index].strip()
    diffs_txt = txt[revs_diffs_sep_index:].strip()

    rev_map = {}
    
    # rev ~= 1.8
    # rev_info ~= {'branches': '', 
    #              'log': 'woot', 
    #              'author': 'derek', 
    #              'diff_len': 5, 
    #              'next': '1.7', 
    #              'commitid': 'sZlUSJolWWw2BbZt', 
    #              'state': 'Exp', 
    #              'date': '2009.08.10.19.50.15'}

    for rev_txt in revs_txt.split('\n\n'):
      rev = rev_txt[:rev_txt.index('\n')].strip()
      rev_info = {}
      for x in [ x.split() for x in rev_txt[rev_txt.index('\n'):].replace('\t',' ').strip().split(';') if x.strip() ]:
        if 'author'==x[0]:
          author = ' '.join( x[1:] )
          if author in settings.ALIASES:
            author = settings.ALIASES[author]
          rev_info['author'] = author
        else:
          rev_info[x[0]] = ' '.join( x[1:] )
      rev_map[rev] = rev_info

    desc_txts = [ x.strip() for x in diffs_txt.replace('\n@@\n','\n@\n@\n').replace('@@','').split('@')[2:] if x.strip() ]
#    print desc_txts
#    sys.exit(0)
    for i in range(len(desc_txts)):
      if desc_txts[i].endswith('\nlog'):
        rev = desc_txts[i].split()[0]
        log = desc_txts[i+1]
        rev_map[rev]['log'] = log
        if i+3<len(desc_txts) and desc_txts[i+2]=='text' and not desc_txts[i+3].endswith('\nlog'):
          rev_map[rev]['txt_len'] = len(desc_txts[i+3])
          rev_map[rev]['txt'] = desc_txts[i+3]
#          print '='*20, rev, log
#          print '*', desc_txts[i+3]
        else:
          rev_map[rev]['txt_len'] = 0
          rev_map[rev]['txt'] = 'unknown'
          
    # datetime-ify datetime strings
    for rev, rev_info in rev_map.iteritems():
      x = [int(x) for x in rev_info['date'].split('.')]
      d = datetime.datetime(x[0], x[1], x[2], x[3], x[4], x[5])
      rev_info['date'] = d + datetime.timedelta(0,settings.TZ_OFFSET*60*60)
    

    for rev, rev_info in rev_map.iteritems():
      if len(rev.split('.'))==2:
        rev_info['prev'] = rev_info['next']
      else:
        if rev.split('.')[-1]=='1':
          rev_info['prev'] = '.'.join(rev.split('.')[:-2])
        else:
          revi = [ int(x) for x in rev.split('.') ]
          revi[-1] -= 1
          revs = '.'.join([ str(x) for x in revi ])
          rev_info['prev'] = revs
          
          
      
    # calc diff lengths per commit
    highest = [0,0]
    for rev, rev_info in rev_map.iteritems():
      if len(rev.split('.'))>2:
        rev_info['diff'] = rev_info['txt']
        rev_info['loc'] = self._get_loc_from_diff(rev_info['diff'])
        rev_info['diff_len'] = len(rev_info['diff'])
      else:
        revi = [ int(x) for x in rev.split('.') ]
        if revi > highest:
          highest = revi
        if rev_info['prev']:
          print rev_info['prev'], '=>', rev
          rev_info['diff'] = rev_map[rev_info['prev']]['txt']
          loc = list( self._get_loc_from_diff(rev_info['diff']) )
          loc.reverse()
          rev_info['loc'] = tuple(loc)
          print "rev_info['loc']", rev_info['loc']
        else:
          rev_info['diff'] = 'unknown'
          rev_info['loc'] = (0,0,0)
        rev_info['diff_len'] = 0
    print 'highest', highest
      
    return filename, rev_map
    
    
  def _get_loc_from_diff(self, diff):
    a, d, c = 0,0,0
    if diff=='unknown':
      return a, c, d
      
    last_delete_line_number = None
    last_delete_line_count = 0
    diff = diff.split('\n')
#    print diff
    i = 0
    while i<len(diff):
      s = diff[i]
#      print 's^^', s, '$$'
#      print diff[i+1]
      flag = s[0]
      line_number = int(s.split()[0][1:])
      line_count = int(s.split()[1])
      actual_line_count = 0
      if flag=='a':
        for j in range(line_count):
#          print len(diff), i, j, i+j
          if i+j<len(diff):
            if diff[i+j].strip():
              actual_line_count+=1
        i += line_count+1
      else:
        actual_line_count = line_count
        i += 1
        
    
      if flag=='a':
        if line_number==last_delete_line_number:
          if actual_line_count < last_delete_line_count:
            d -= actual_line_count
            c += actual_line_count
          else:
            d -= last_delete_line_count
            c += last_delete_line_count
            a += actual_line_count - last_delete_line_count
        else:
          a += actual_line_count
      if flag=='d':
        d += actual_line_count
        last_delete_line_number = line_number
        last_delete_line_count = actual_line_count
    return a, c, d
    
    
class HTMLFile(object):

  def __init__(self, fn, title):
    self.f = open(fn,'w')
    print fn
    self.f.write('<html>')
    self.f.write('<head><link rel="stylesheet" href="pystatcvs.css" type="text/css"/></head>')
    self.f.write('<body style="margin:1em;">')
    self.f.write('<div style="background:#CCCCBB; padding:2em; margin:-1em; margin-bottom:0em;">')
    self.f.write('<span style="float:right;"><a href="index.html">home</a></span>')
    self.f.write('<b style="font-size:larger;">PyStatCVS</b> &nbsp; ')
    self.f.write('<span style="font-size:smaller;">by <a href="mailto:derek.anderson@jerichosystems.com">Derek Anderson</a></span>')
    self.f.write('</div>')
  
  
  def write(self, *args, **kwargs):
    self.f.write(*args, **kwargs)
    
    
  def close(self):
    self.f.write('<center style="clear:both; font-size:smaller;">Generated: %s</center>' % datetime.datetime.now())
    self.f.write('</body></html>')
    self.f.close()


if __name__=='__main__':
  x = PyStatCVS(settings.CVSROOT, settings.VIEWVC_BASE_URL)
  x.get_stats()
  x.gen_report(settings.OUTPUT_DIR)
  
